#include "frpcbase64.h"

namespace FRPC {
namespace {
const unsigned char base64Table[256] = {
           // 000 - 007
           0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 008 - 015
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 016 - 023
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 024 - 031
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 032 - 039
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 040 - 047      '+'                     '/
           0x80, 0x80, 0x80, 0x3e, 0x80, 0x80, 0x80, 0x3f,
           // 048 - 055      '0'   '1'   '2'   '3'   '4'
           0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
           // 056 - 063
           //'5' '6'   '7'   '8'   '9'
           0x3c, 0x3d, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 064 - 072
           //    'A'   'B'   'C'   'D'   'E'   'F'   'G'
           0x80, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
           // 072 - 079
           //'H' 'I'   'J'   'K'   'L'   'M'   'N'   'O'
           0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
           // 080 - 087
           //'P' 'Q'   'R'   'S'   'T'   'U'   'V'   'W'
           0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
           // 088 - 095
           //'X' 'Y'   'Z'
           0x17, 0x18, 0x19, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 096 - 103
           //    'a'   'b'   'c'   'd'   'e'   'f'   'g'
           0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
           // 104 - 111
           //'h' 'i'   'j'   'k'   'l'   'm'   'n'   'o'
           0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
           // 112 - 119
           //'p' 'q'   'r'   's'   't'   'u'   'v'   'w'
           0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
           // 120 - 127
           //'x' 'y'   'z'
           0x31, 0x32, 0x33, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 128 - 135
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 136 - 143
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 144 - 151
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 152 - 159
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 160 - 167
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 168 - 175
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 176 - 183
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 184 - 191
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 192 - 199
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 200 - 207
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 208 - 215
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 216 - 223
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 224 - 231
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 232 - 239
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 240 - 247
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
           // 248 - 255
           0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
       };
} // namespace

const std::string Base64::decode(const char *data, long len) {
    Base64 decoder;
    return decoder.process(data, len);
}

Base64::Base64() {
    reset();
}

void Base64::reset() {
    i = 0;
    for (size_t seq = 0; seq < 4; ++seq) {
        a[seq] = '=';
        b[seq] = 0;
    }
}

std::string Base64::process(const char *data, long len)
{
    std::string result;
    result.reserve((len * 3) / 4);

    for (const char
             *isrc = data,
             *end = data + len;
            isrc != end; )
    {
        for (; (isrc != end) && (i < 4); isrc++) {
            // a hack. should be based on the table values
            if (isspace(*isrc))
                continue;
            b[i] = base64Table[a[i] = *isrc];
            i++;
        }

        // not a complete sequence.
        // pre-decoded the valid part into b[] and a[]. Just return
        if (i < 4)
            break;

        // when nothing read terminate this loop (invalid b64 chars)
        if (!i)
            break;

        unsigned char o[4];
        o[0] = (b[0] << 2) | (b[1] >> 4);
        o[1] = (b[1] << 4) | (b[2] >> 2);
        o[2] = (b[2] << 6) | b[3];

        int len = (a[2] == '=') ? 1 : ((a[3] == '=') ? 2 : 3);
        result.append((char *)o, len);

        reset();

        if (len < 3)
            break;
    }

    return  result;
}

} // namespace FRPC
